<template>
  <div class="upload-excel">
    <div class="btn-upload">
      <!--handleUpload触发隐藏域的点击事件-->
      <el-button
          :loading="loading"
          type="primary"
          @click="handleUpload"
      >
        {{ $t("msg.uploadExcel.upload") }}
      </el-button>
    </div>
    <!--隐藏域 用css把它隐藏掉 handleChange是选中文件之后的回调事件-->
    <input
        ref="excelUploadInput"
        class="excel-upload-input"
        type="file"
        accept=".xlsx, .xls"
        @change="handleChange"
    />
    <!-- https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_Drag_and_Drop_API -->
    <div
        class="drop"
        @drop.stop.prevent="handleDrop"
        @dragover.stop.prevent="handleDragover"
        @dragenter.stop.prevent="handleDragover"
    >
      <i class="el-icon-upload" />
      <span>{{ $t("msg.uploadExcel.drop") }}</span>
    </div>
  </div>
</template>

<script setup>
import XLSX from "xlsx";
import { ref } from "vue";
import { getHeaderRow, isExcel } from './utils'
import { ElMessage } from 'element-plus'
const props = defineProps({
  // 上传前回调
  beforeUpload: Function,
  // 成功回调
  onSuccess: Function
});

/**
 * 点击上传触发
 */
const loading = ref(false);
const excelUploadInput = ref(null);
const handleUpload = () => {
  excelUploadInput.value.click();
};
// 触发input的点击事件后，选中文件后会触发handleChange，通过e.target.files获取到选中的文件
const handleChange = (e) => {
  const files = e.target.files;
  const rawFile = files[0]; // only use files[0]
  if (!rawFile) return; // 如果当前选中的文件不存在
  upload(rawFile);
};

/**
 * 触发上传事件
 */
const upload = (rawFile) => {
  // 把隐藏域选中的值给清空掉
  excelUploadInput.value.value = null;
  // 如果没有指定上传前回调的话
  if (!props.beforeUpload) {
    // 直接读取数据
    readerData(rawFile);
    return;
  }
  // 如果指定了上传前回调，那么只有返回 true 才会执行后续操作 比如文件格式不对或大小不合适等等一些过滤操作。
  const before = props.beforeUpload(rawFile);
  if (before) {
    readerData(rawFile);
  }
};

/**
 * 读取数据（异步）
 * 注意：reader.readAsArrayBuffer()一定要先读取，读取完成之后才会进入reader.onload = e => {}，
 * reader.onload一定要写在reader.readAsArrayBuffer()前面。
 */
const readerData = (rawFile) => {
  loading.value = true;
  return new Promise((resolve, reject) => {
    // 文件读取类 https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
    const reader = new FileReader();
    // 该事件在读取操作完成时触发
    // https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/onload
    reader.onload = (e) => {
      // 1. 获取解析到的数据
      const data = e.target.result;
      // 2. 利用 XLSX 对数据进行解析
      const workbook = XLSX.read(data, { type: "array" });
      // 3. 获取第一张表格(工作簿)名称
      const firstSheetName = workbook.SheetNames[0];
      // 4. 只读取 Sheet1（第一张表格）的数据
      const worksheet = workbook.Sheets[firstSheetName];
      // 5. 解析数据表头
      const header = getHeaderRow(worksheet);
      // 6. 解析数据体
      const results = XLSX.utils.sheet_to_json(worksheet);
      // 7. 传入解析之后的数据
      generateData({ header, results });
      // 8. loading 处理
      loading.value = false;
      // 9. 异步完成
      resolve();
    };
    // 启动读取指定的 Blob 或 File 内容
    reader.readAsArrayBuffer(rawFile);
  });
};

/**
 * 根据导入内容，生成数据
 */
const generateData = (excelData) => {
  props.onSuccess && props.onSuccess(excelData);
};

/**
 * 拖拽文本释放时触发
 */
const handleDrop = e => {
  // 上传中跳过
  if (loading.value) return
  const files = e.dataTransfer.files
  if (files.length !== 1) {
    ElMessage.error('必须要有一个文件')
    return
  }
  const rawFile = files[0]
  if (!isExcel(rawFile)) {
    ElMessage.error('文件必须是 .xlsx, .xls, .csv 格式')
    return false
  }
  // 触发上传事件
  upload(rawFile)
}

/**
 * 拖拽悬停时触发
 */
const handleDragover = e => {
  // https://developer.mozilla.org/zh-CN/docs/Web/API/DataTransfer/dropEffect
  // 在新位置生成源项的副本或者说复制源数据到目标位置
  // 这行代码的作用是告诉浏览器：“当前拖放操作的效果是 复制”。
  // 用户会看到鼠标光标可能变成一个带有“+”号的箭头（表示复制），而不是默认的移动箭头。
  e.dataTransfer.dropEffect = 'copy'
}
</script>

<style lang="scss" scoped>
.upload-excel {
  display: flex;
  justify-content: center;
  margin-top: 100px;
  .excel-upload-input {
    display: none;
    z-index: -9999;
  }
  .btn-upload,
  .drop {
    border: 1px dashed #bbb;
    width: 350px;
    height: 160px;
    text-align: center;
    line-height: 160px;
  }
  .drop {
    line-height: 60px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    color: #bbb;
    i {
      font-size: 60px;
      display: block;
    }
  }
}
</style>